{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "4.1. 模型构造.ipynb",
      "provenance": [],
      "collapsed_sections": [],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/chongzicbo/Dive-into-Deep-Learning-tf.keras/blob/master/4.1.%20%E6%A8%A1%E5%9E%8B%E6%9E%84%E9%80%A0.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ZEZBlI9q0uPA",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 62
        },
        "outputId": "dd256258-1b84-42f8-f0b0-177c6a89d5b0"
      },
      "source": [
        "import tensorflow as tf\n",
        "from tensorflow import keras"
      ],
      "execution_count": 1,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              "<p style=\"color: red;\">\n",
              "The default version of TensorFlow in Colab will soon switch to TensorFlow 2.x.<br>\n",
              "We recommend you <a href=\"https://www.tensorflow.org/guide/migrate\" target=\"_blank\">upgrade</a> now \n",
              "or ensure your notebook will continue to use TensorFlow 1.x via the <code>%tensorflow_version 1.x</code> magic:\n",
              "<a href=\"https://colab.research.google.com/notebooks/tensorflow_version.ipynb\" target=\"_blank\">more info</a>.</p>\n"
            ],
            "text/plain": [
              "<IPython.core.display.HTML object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "4SduYvhZ0_D9",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "tf.enable_eager_execution()#启用计算图"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "K5UyRgCK4XXf",
        "colab_type": "text"
      },
      "source": [
        "###4.1.1 继承keras.layers.Layer来构建新的神经网络层\n",
        "&emsp;&emsp;在keras中，构建新的神经网络层可以通过继承keras.layers.Layer类来实现。你只需要实现三个方法即可:\n",
        "\n",
        "* build(input_shape): 这是你定义权重的地方。这个方法必须设 self.built = True，可以通过调用 super([Layer], self).build() 完成。\n",
        "* call(x): 这里是编写层的功能逻辑的地方。你只需要关注传入 call 的第一个参数：输入张量，除非你希望你的层支持masking。\n",
        "* compute_output_shape(input_shape): 如果你的层更改了输入张量的形状，你应该在这里定义形状变化的逻辑，这让Keras能够自动推断各层的形状。\n",
        "\n",
        "&emsp;&emsp;这里我们通过集成两个Dense层来构造一个MLP网络层。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Jre-efuM1HP7",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "class MLP(keras.layers.Layer):\n",
        "  def __init__(self):\n",
        "    super(MLP,self).__init__()\n",
        "    self.hidden=keras.layers.Dense(256,activation='relu') #隐藏层\n",
        "    self.outputs=keras.layers.Dense(10) #输出层\n",
        "\n",
        "  def build(self,input_shape):\n",
        "    print(f\"{self.name} build() is called.\")\n",
        "    self.build=True\n",
        "\n",
        "  def call(self,inputs):\n",
        "    print(f\"{self.name} call() is called.\")\n",
        "    x=self.hidden(inputs)\n",
        "    x=self.outputs(x)\n",
        "    return x"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hvUZhWMZ6DeW",
        "colab_type": "text"
      },
      "source": [
        "&emsp;&emsp;构造完自己的神经网络层后，可以将其实例化后输入一个张量，并获得一个张量输出。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "kJba-z7g25Di",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 151
        },
        "outputId": "b5f256b9-1796-4d36-a169-b9f64772703b"
      },
      "source": [
        "x=tf.random.normal(shape=(2,20))\n",
        "mlp=MLP()(x)\n",
        "mlp"
      ],
      "execution_count": 4,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "mlp build() is called.\n",
            "mlp call() is called.\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<tf.Tensor: id=59, shape=(2, 10), dtype=float32, numpy=\n",
              "array([[-0.30091882, -0.07629362, -0.71176815,  0.4058078 , -0.14221677,\n",
              "        -0.04297486, -0.07796003, -0.23001648, -0.18110617, -0.54717225],\n",
              "       [-0.2731866 , -0.1405192 , -0.2932847 ,  0.35041004, -0.2668861 ,\n",
              "        -0.01136987, -0.15359104, -0.36639145,  0.15943211, -0.47985178]],\n",
              "      dtype=float32)>"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 4
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fKyohnZW-U2j",
        "colab_type": "text"
      },
      "source": [
        "###4.1.2. 继承layers.Model类来构造模型\n",
        "&emsp;&emsp;在keras中，除了Sequential顺序模型和使用函数式API的Model类模型这两类模型之外，你还可以通过继承Model类并在call方法中实现你自己的前向传播，以创建你自己的完全定制化的模型。这里是一个用 Model类继承写的简单的多层感知器的例子："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "7SB6fGbU3M5y",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "class MLPModel(keras.Model):\n",
        "  def __init__(self):\n",
        "    super(MLPModel,self).__init__()\n",
        "    self.hidden=keras.layers.Dense(256,activation='relu') #隐藏层\n",
        "    self.out=keras.layers.Dense(10) #输出层\n",
        "\n",
        "  def call(self,inputs):\n",
        "    return self.out(self.hidden(x)) #前向传播"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "LQTCSGuA_WCT",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "net=MLPModel()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "GZ3InPCl_i57",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "03cbba38-1356-41fd-d0f4-34ba21fa747f"
      },
      "source": [
        "net.layers[0].name"
      ],
      "execution_count": 7,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'dense_2'"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 7
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "lVgu2Fs0_nm2",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "net.compile(optimizer=keras.optimizers.Adam(learning_rate=0.001),loss=keras.losses.CategoricalCrossentropy)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lvCmLdVpBERS",
        "colab_type": "text"
      },
      "source": [
        "###4.1.3 keras.layers.Layer和keras.Model的区别\n",
        "&emsp;&emsp;从它们的名称可以看出，Layer是一个层，而Model是一个模型；前者用于构造神经网络层，后者用于构建神经网络模型，可以说Layer由于构建Model，而Model包含各种Layer。一个层接受一个张量并给出一个张量，这是一些张量运算的结果而模型是多层的组合。\\\n",
        "&emsp;&emsp;如果使用现有的keras / tf层构建新的模型体系结构，则构建自定义模型。\n",
        "\n",
        "如果要在图层中实现自己的自定义张量操作，则构建自定义图层。如果在自定义图层中使用非张量操作，则必须编码图层如何向前传播和向后传播。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LBy2RM0Kpa7B",
        "colab_type": "text"
      },
      "source": [
        "###4.1.4. 小结\n",
        "* 可以通过继承Layer类来构造自定义神经网络层。\n",
        "* 可以通过继承Model类来构建神经网络模型。\n",
        "* Layer是网络层，而Model是神经网络模型，两者有所不同。"
      ]
    }
  ]
}